package xyz.hubery.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import xyz.hubery.domain.*;
import xyz.hubery.dto.MarketDto;
import xyz.hubery.dto.UpdateAccountDTO;
import xyz.hubery.feign.MarketServiceFeign;
import xyz.hubery.mapper.AccountMapper;
import xyz.hubery.mappers.AccountMappers;
import xyz.hubery.service.AccountDetailService;
import xyz.hubery.service.AccountService;
import xyz.hubery.service.CoinService;
import xyz.hubery.service.ConfigService;
import xyz.hubery.vo.AccountVo;
import xyz.hubery.vo.SymbolAssetVo;
import xyz.hubery.vo.UserTotalAccountVo;

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Autowired
    private CoinService coinService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private AccountDetailService accountDetailService;
    @Autowired
    private MarketServiceFeign marketServiceFeign;

    @Override
    public Account getUserAccountByCoinName(Long userId, String coinName) {
        // 根据币种名称查询币种ID
        Coin coin = coinService.getCoinByName(coinName);
        if (coin == null) {
            throw new IllegalArgumentException("该币种不存在");
        }
        Account account = getOne(new LambdaQueryWrapper<Account>().eq(Account::getUserId, userId).eq(Account::getCoinId, coin.getId()));
        if (account == null) {
            throw new IllegalArgumentException("该用户不存在该币种的账户");
        }
        // 设置买入率和卖出率
        Config sellRateConfig = configService.getConfigByCode("USDT2CNY");
        Config buyRateConfig = configService.getConfigByCode("CNY2USDT");

        account.setSellRate(new BigDecimal(sellRateConfig.getValue()));
        account.setBuyRate(new BigDecimal(buyRateConfig.getValue()));
        return account;
    }

    /**
     * 暂时锁定用户的资产
     * （冻结资金）
     *
     * @param userId  用户的 id
     * @param coinId  币种的 id
     * @param mum     锁定的金额
     * @param type    资金流水的类型
     * @param orderId 订单的 Id
     * @param fee     手续费
     */
    @Transactional
    @Override
    public boolean lockUserAmount(Long userId, Long coinId, BigDecimal mum, String type, Long orderId, BigDecimal fee) {
        Account account = getUserAccountByCoinId(userId, coinId);
        if (account == null) {
            throw new IllegalArgumentException("您输入的资产类型不存在");
        }
        BigDecimal balanceAmount = account.getBalanceAmount(); // 账户余额
        if (balanceAmount.compareTo(mum) < 0) {  // 库存的操作
            throw new IllegalArgumentException("账号的资金不足");
        }
        account.setBalanceAmount(balanceAmount.subtract(mum).subtract(fee)); // 扣减余额
        account.setFreezeAmount(account.getFreezeAmount().add(mum).add(fee)); // 冻结资金
        boolean updateById = updateById(account);
        if (updateById) { // 增加流水记录
            return accountDetailService.addAccountDetail(account, orderId, (byte) 2, type, mum, fee, "冻结资金");
        }
        return updateById;
    }

    /**
     * 修改账户余额
     *
     * @param adminId  操作员ID
     * @param userId   用户ID
     * @param coinName 币种名称
     * @param orderId  订单ID
     * @param mum      变动金额
     * @param fee      手续费
     * @param type     业务类型
     * @param remark   流水状态
     * @return
     */
    @Transactional
    @Override
    public boolean transferAmount(Long adminId, Long userId, String coinName, Long orderId, BigDecimal mum, BigDecimal fee, String type, String remark) {
        // 查询账户流水对象
        Account account = getUserAccountByCoinName(userId, coinName);
        if (account == null) {
            throw new IllegalArgumentException("该用户不存在该币种的账户");
        }
        // 添加一条流水记录
        AccountDetail accountDetail = new AccountDetail();
        accountDetail.setUserId(userId);
        accountDetail.setCoinId(account.getCoinId());
        accountDetail.setAccountId(account.getId());
        accountDetail.setRefAccountId(account.getId());
        accountDetail.setOrderId(orderId);
        accountDetail.setDirection((byte) 1);
        accountDetail.setBusinessType(type);
        accountDetail.setAmount(mum);
        accountDetail.setFee(fee);
        accountDetail.setRemark(remark);
        boolean save = accountDetailService.save(accountDetail);
        if (save) { // 添加成功，修改账户余额
            account.setBalanceAmount(account.getBalanceAmount().add(mum));
            account.setRechargeAmount(account.getRechargeAmount().add(mum));
            return updateById(account);
        }
        return save;
    }


    @Transactional
    @Override
    public boolean subAmount(Long adminId, Long userId, Long coinId, Long orderId, BigDecimal mum, BigDecimal fee) {
        // 查询账户信息
        Coin coin = coinService.getById(coinId);
        if (coin == null) {
            throw new IllegalArgumentException("该币种不存在");
        }
        Account account = getUserAccountByCoinName(userId, coin.getName());
        if (account == null) {
            throw new IllegalArgumentException("账户不存在");
        }
        // 新增一条账户流水记录
        AccountDetail accountDetail = new AccountDetail();
        accountDetail.setUserId(userId);//user_id
        accountDetail.setCoinId(coinId);//coin_id
        accountDetail.setAccountId(account.getId());//account_id
        accountDetail.setRefAccountId(account.getId());//ref_account_id
        accountDetail.setOrderId(orderId);//order_id
        accountDetail.setDirection((byte) 2);//direction
        accountDetail.setBusinessType("withdrawals_out");//business_type
        accountDetail.setAmount(mum);//amount
        accountDetail.setFee(fee);//fee
        accountDetail.setRemark("提现");//remark
        boolean save = accountDetailService.save(accountDetail);
        if (save) {
            // 查询账户余额
            BigDecimal balanceAmount = account.getBalanceAmount();
            if (balanceAmount.compareTo(mum) < 0) {
                throw new IllegalArgumentException("余额不足");
            }
            account.setBalanceAmount(balanceAmount.subtract(mum));
            account.setWithdrawalsAmount(account.getWithdrawalsAmount().add(mum));
            return updateById(account);
        }
        return false;
    }

    /**
     * 计算用户的总资产
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public UserTotalAccountVo getUserTotalAccount(Long userId) {
        // 计算总资产
        UserTotalAccountVo userTotalAccountVo = new UserTotalAccountVo();
        BigDecimal basicCoin2CnyRate = BigDecimal.ONE; // 汇率
        BigDecimal basicCoin = BigDecimal.ZERO; // 平台计算币的基币
        List<AccountVo> assertList = new ArrayList<AccountVo>();
        List<Account> accounts = list(new LambdaQueryWrapper<Account>().eq(Account::getUserId, userId));
        if (CollectionUtils.isEmpty(accounts)) {
            userTotalAccountVo.setAssertList(assertList);
            userTotalAccountVo.setAmountUs(BigDecimal.ZERO);
            userTotalAccountVo.setAmount(BigDecimal.ZERO);
            return userTotalAccountVo;
        }

        AccountMappers mappers = AccountMappers.INTSANCE;
        // 获取所有的币种
        for (Account account : accounts) {
            AccountVo accountVo = mappers.toAccountVo(account);
            Long coinId = account.getCoinId();
            Coin coin = coinService.getById(coinId);
            if (coin == null || coin.getStatus() != (byte) 1) {
                continue;
            }
            // 设置币的信息
            accountVo.setCoinName(coin.getName());
            accountVo.setCoinImgUrl(coin.getImg());
            accountVo.setCoinType(coin.getType());
            accountVo.setWithdrawFlag(coin.getWithdrawFlag());
            accountVo.setRechargeFlag(coin.getRechargeFlag());
            accountVo.setFeeRate(BigDecimal.valueOf(coin.getRate()));
            accountVo.setMinFeeNum(coin.getMinFeeNum());
            // 计算总的账面余额
            BigDecimal volume = accountVo.getBalanceAmount().add(accountVo.getFreezeAmount());
            accountVo.setCarryingAmount(volume); // 总的账面余额
            assertList.add(accountVo);

            // 将该币和我们系统统计币使用的基币转化
            BigDecimal currentPrice = getCurrentCoinPrice(coinId); // 相当于汇率

            BigDecimal total = volume.multiply(currentPrice);
            basicCoin = basicCoin.add(total); // 将该子资产添加到我们的总资产里面
        }

        userTotalAccountVo.setAmount(basicCoin.multiply(basicCoin2CnyRate).setScale(8, RoundingMode.HALF_UP)); // 人民币总资产
        userTotalAccountVo.setAmountUs(basicCoin); // 平台计价总资产
        userTotalAccountVo.setAmountUsUnit("GCN"); // 平台计价单位
        userTotalAccountVo.setAssertList(assertList);

        return userTotalAccountVo;
    }

    /**
     * 获取当前币的价格
     * 使用我们的基币兑换该币的价格
     *
     * @param coinId
     * @return
     */
    private BigDecimal getCurrentCoinPrice(Long coinId) {
        // 1 查询我们的基础币是什么
        Config configBasicCoin = configService.getConfigByCode("PLATFORM_COIN_ID"); // 基础币
        if (configBasicCoin == null) {
            throw new IllegalArgumentException("请配置基础币后使用");
        }
        Long basicCoinId = Long.valueOf(configBasicCoin.getValue());
        if (coinId.equals(basicCoinId)) { // 该币就是基础币
            return BigDecimal.ONE;
        }
        // 不等于,需要查询交易市场 ,使用基础币作为报价货币,使用报价货币的的金额来计算当前币的价格
        MarketDto market = marketServiceFeign.getMarket(coinId, basicCoinId);
        if (market != null) { // 存在交易对
            return market.getOpenPrice();
        } else {
            // 该交易对不存在
            log.error("不存在当前币和平台币兑换的市场,请后台人员及时添加");
            return BigDecimal.ZERO;//TODO
        }
    }

    @Transactional
    @Override
    public boolean unFreeze(Long userId, Long coinId, BigDecimal freeze) {
        Coin coin = coinService.getById(coinId);
        if (coin == null) {
            throw new IllegalArgumentException("币种异常");
        }
        // 查询账户信息
        Account account = getUserAccountByCoinId(userId, coinId);
        if (account == null || account.getStatus() == 2) {
            throw new IllegalArgumentException("账户异常");
        }

        account.setBalanceAmount(account.getBalanceAmount().add(freeze));
        account.setFreezeAmount(account.getFreezeAmount().subtract(freeze));
        return updateById(account);
    }

    /**
     * 查询用户交易对资产
     *
     * @param userId
     * @param symbol 交易对标识
     * @return
     */
    @Override
    public SymbolAssetVo getUserAssetBySymbol(Long userId, String symbol) {
        SymbolAssetVo symbolAssetVo = new SymbolAssetVo();
        // 根据标识查询交易市场
        MarketDto market = marketServiceFeign.findBySymbol(symbol);


        Coin buyCoin = coinService.getById(market.getBuyCoinId()); // 报价货币
        Account buyAccount = getUserAccountByCoinName(userId, buyCoin.getName()); // 报价货币资产
        symbolAssetVo.setBuyAmount(buyAccount.getBalanceAmount());
        symbolAssetVo.setBuyUnit(buyCoin.getName());
        symbolAssetVo.setBuyLockAmount(buyAccount.getFreezeAmount());
        symbolAssetVo.setBuyFeeRate(market.getFeeBuy());


        Coin sellCoin = coinService.getById(market.getSellCoinId()); // 基础货币
        Account sellAccount = getUserAccountByCoinName(userId, sellCoin.getName()); //基础货币资产
        symbolAssetVo.setSellAmount(sellAccount.getBalanceAmount());
        symbolAssetVo.setSellUnit(sellCoin.getName());
        symbolAssetVo.setSellLockAmount(sellAccount.getFreezeAmount());
        symbolAssetVo.setSellFeeRate(market.getFeeSell());

        return symbolAssetVo;
    }


    /**
     * 根据成交记录更新账户余额信息
     * 总共需要更新四个账户余额信息
     */
    @Transactional
    @Override
    public boolean transferAmount(UpdateAccountDTO accountDTO) {
        Long sellCoinId = accountDTO.getSellCoinId(); // 基础货币id
        Long buyCoinId = accountDTO.getBuyCoinId(); // 报价货币id
        BigDecimal amountBTC = accountDTO.getVolume(); // 基础货币变化金额
        BigDecimal amountGCN = accountDTO.getTurnover(); // 报价货币变化金额

        long orderId = IdWorker.getId(); //当前订单id
        String bussinessType = "order_turnover";

        // 1、更新买方基础货币账户（买方BTC账户--->买入BTC，花费GCN
        Account account1 = getUserAccountByCoinId(accountDTO.getBuyUserId(), sellCoinId);
        account1.setBalanceAmount(account1.getBalanceAmount().add(amountBTC)); // 买方买入BTC
        accountDetailService.addAccountDetail(account1, orderId, (byte) 1, bussinessType, amountBTC, BigDecimal.ZERO, "买入");

        // 2、更新买方报价货币账户（买方GCN账户--->买入BTC，花费GCN
        Account account2 = getUserAccountByCoinId(accountDTO.getBuyUserId(), buyCoinId);
        BigDecimal buyFee = accountDTO.getBuyFeeRate().multiply(amountGCN); // 手续费
        account2.setFreezeAmount(account2.getFreezeAmount().subtract(amountGCN).subtract(buyFee)); // 买方卖出GCN,扣减冻结资金中的GCN（卖出的+手续费）
        if (accountDTO.getBuyStatus() == (byte) 2) { // 该订单全部处理完成，解冻剩余资金
            //TODO
            account2.setBalanceAmount(account2.getBalanceAmount().add(accountDTO.getBuyFreeze())); // 解冻本次委托时多冻结的部分
            account2.setFreezeAmount(account2.getFreezeAmount().subtract(accountDTO.getBuyFreeze()));
        }
        accountDetailService.addAccountDetail(account2, orderId, (byte) 2, bussinessType, amountGCN, buyFee, "卖出");

        // 3、更新卖方基础货币账户（卖方BTC账户--->卖出BTC，获得GCN
        Account account3 = getUserAccountByCoinId(accountDTO.getSellUserId(), sellCoinId);
        BigDecimal sellFee = accountDTO.getSellFeeRate().multiply(amountBTC); // 手续费
        account3.setFreezeAmount(account3.getFreezeAmount().subtract(amountBTC).subtract(sellFee)); // 卖方卖出BTC
        if (accountDTO.getSellStatus() == (byte) 2) { // 该订单全部处理完成，解冻剩余资金
            //TODO
            account3.setBalanceAmount(account3.getBalanceAmount().add(accountDTO.getSellFreeze())); // 解冻本次委托时多冻结的部分
            account3.setFreezeAmount(account3.getFreezeAmount().subtract(accountDTO.getSellFreeze()));
        }
        accountDetailService.addAccountDetail(account3, orderId, (byte) 2, bussinessType, amountBTC, sellFee, "卖出");

        // 4、更新卖方报价货币账户（卖方GCN账户--->卖出BTC，获得GCN
        Account account4 = getUserAccountByCoinId(accountDTO.getSellUserId(), buyCoinId);
        account4.setBalanceAmount(account4.getBalanceAmount().add(amountGCN));
        accountDetailService.addAccountDetail(account4, orderId, (byte) 1, bussinessType, amountGCN, BigDecimal.ZERO, "买入");

        // 更新
        return updateBatchById(Arrays.asList(account1, account2, account3, account4));
    }

    @Transactional
    @Override
    public Account getUserAccountByCoinId(Long userId, Long coinId) {
        return getOne(new LambdaQueryWrapper<Account>()
                .eq(Account::getUserId, userId).eq(Account::getCoinId, coinId)
                .last("FOR UPDATE") // 添加行锁
        );
    }


}
